<template>
  <alert :alertData="alertData" :opacity="alertOpacity" />

  <div class="div-content-wrapper">
    <!-- 搜索框容器 -->
    <div class="div-controls-wrapper">
      <!-- 搜索框第一行-实体搜索 -->
      <div class="d-flex w-100">
        <hwmi-text-input
          class="flex-grow-1"
          placeholder="“感冒..疾病”"
          v-model="entityName"
          @click="inEntityNameClick"
          @keydown.enter="btnQueryClick"
        />

        <button
          class="btn-route"
          @click="btnShowRouteClick"
          data-bs-toggle="tooltip"
          data-bs-placement="bottom"
          data-bs-html="true"
          title="<small>实体路径</small>"
        >
          <i
            class="fa-solid fa-code-merge"
            style="color: #0d6efd"
            v-show="!isRouteSearchShow"
          ></i>
          <i
            class="fa-solid fa-xmark"
            style="color: #0d6efd"
            v-show="isRouteSearchShow"
          ></i>
        </button>

        <button
          class="btn-query"
          @click="btnQueryClick"
          data-bs-toggle="tooltip"
          data-bs-placement="bottom"
          data-bs-html="true"
          title="<small>图谱查询</small>"
        >
          <i class="fa-solid fa-magnifying-glass" style="color: white"></i>
        </button>
      </div>

      <!-- 搜索框第二行-搜索设置 -->
      <div
        class="mt-2 div-search-wrapper"
        v-if="isSearchSettingsShow"
      >
        <div class="form-check form-switch div-switch-wrapper 
        d-flex justify-content-center align-items-center">
          <label class="form-check-label" for="in-neighbor-switch">一阶邻居</label>
          <input class="form-check-input in-neighbor-switch" 
          type="checkbox" role="switch" id="in-neighbor-switch"
          v-model="isNeighbor2">
          <label class="form-check-label" for="in-neighbor-switch">二阶邻居</label>
        </div>
      </div>

      <!-- 搜索框第二行-路径搜索 -->
      <div
        class="mt-2 div-search-wrapper d-flex align-items-center"
        v-if="isRouteSearchShow"
      >
        <i
          class="fa-solid fa-arrows-rotate mx-3"
          style="color: #0d6efd; cursor: pointer"
          @click="exchangeOrigDest"
        ></i>

        <div class="flex-grow-1 div-route-input-wrapper">
          <!-- 起点输入栏 -->
          <div
            class="d-flex align-items-center"
            style="border-bottom: 1px solid #eee"
          >
            <i class="fa-solid fa-location-dot" style="color: #83e083"></i>
            <hwmi-text-input
              class="in-route-orig-dest flex-grow-1"
              placeholder="在图谱上选择起点，或输入实体名称"
              v-model="routeOrig"
              @click="inRouteOrigClick"
            />
          </div>

          <!-- 终点输入栏 -->
          <div class="d-flex align-items-center">
            <i class="fa-solid fa-location-dot" style="color: #ff8e8e"></i>
            <hwmi-text-input
              class="in-route-orig-dest flex-grow-1"
              placeholder="选择终点"
              v-model="routeDest"
              @click="inRouteDestClick"
            />
          </div>
        </div>

        <button class="btn-submit-route-search" @click="btnRouteSearchClick">
          查找<br />路径
        </button>
      </div>
    </div>

    <div class="div-graph-wrapper">
      <!-- 知识图谱界面上方的加载进度条 -->
      <div class="progress div-graph-progress" v-if="isLoading">
        <div
          class="progress-bar"
          role="progressbar"
          style="background-color: #00b4ff"
          :style="{ width: graphTransitionProgress + '%' }"
        ></div>
      </div>
      <div id="div-graph-container" class="div-graph-container"></div>
    </div>
  </div>

  <hwmi-dialog
    title="实体详情"
    :style="{ opacity: dialogOpacity, width: dialogWidth,fontSize:'14px' }"
    :show="isDialogShow"
    @close="hideEntityDetail"
  >
    <!-- 实体名称和类型 -->
    <div class="p-2">
      <b>实体:{{ graphData.nodes[selectedNodeIndex].labelUnwrapped }}</b>
      <br />
      <b>实体类型:{{ graphData.nodes[selectedNodeIndex].type }}</b>
    </div>

    <div class="div-split-line"></div>

    <!-- 实体属性 -->
    <div
      class="d-flex flex-column p-2"
      v-if="graphData.nodes[selectedNodeIndex].properties.length > 0"
    >
      <b>【实体属性】</b>
      <div
        v-for="(item, index) in graphData.nodes[selectedNodeIndex].properties"
        :key="index"
      >
        <label>
          <b>{{ item.name }}:</b> {{ item.text }}
        </label>
      </div>
    </div>

    <div class="p-2" v-else>该结点暂无属性</div>

    <div class="div-split-line"></div>

    <!-- 实体一二阶邻居 -->
    <div
      class="d-flex flex-column p-2"
      v-if="graphData.nodes[selectedNodeIndex].neighbor1.length > 0"
    >
      <b>【一阶邻居】</b>
      <div
        v-for="(item, index) in graphData.nodes[selectedNodeIndex].neighbor1"
        :key="index"
      >
        <label>{{ item }} </label>
        <i class="i-search-neighbor fa-solid fa-magnifying-glass ms-1"
        @click="setSearchEntity(item)"></i>
      </div>
    </div>
    <div class="p-2" v-else>该结点暂无一阶邻居</div>

    <div
      class="d-flex flex-column p-2"
      v-if="graphData.nodes[selectedNodeIndex].neighbor2.length > 0"
    >
      <b>【二阶邻居】</b>
      <div
        v-for="(item, index) in graphData.nodes[selectedNodeIndex].neighbor2"
        :key="index"
      >
        <label>{{ item }} </label>
        <i class="i-search-neighbor fa-solid fa-magnifying-glass ms-1"
        @click="setSearchEntity(item)"></i>
      </div>
    </div>
    <div class="p-2" v-else>该结点暂无二阶邻居</div>

    <div class="w-100 d-flex justify-content-center my-2">
      <button type="button" class="btn btn-primary"
        @click="setSearchEntity(graphData.nodes[selectedNodeIndex].labelUnwrapped)">
        <i class="fa-solid fa-magnifying-glass"></i>
        搜索此实体
      </button>
    </div>
  </hwmi-dialog>
</template>

<script>
import axios from "axios";

import { initGraph, updateGraph } from "../modules/zstp/GraphOperations";
import { wrapNodeLabel } from "../modules/zstp/WrapNodeLabel";

import ANIMATIONS_COMMON from "../modules/AnimationsCommon";
import ANIMATIONS from "../modules/zstp/Animations";
import { HWMI_URLS } from "../modules/Api";
import { isMobileBrowser } from "@/modules/Utilities";

import { Tooltip } from "bootstrap";

import HwmiTextInput from "./Common/hwmi-text-input.vue";
import Alert from "./Common/alert.vue";
import HwmiDialog from "./Common/hwmi-dialog.vue";

const ZSTP_STATES = {
  STATE_DISPLAY_GRAPH: 0,
  STATE_ROUTE_ENTER_ORIG: 1,
  STATE_ROUTE_ENTER_DEST: 2,
};

export default {
  components: {
    Alert,
    HwmiTextInput,
    HwmiDialog,
  },
  data: () => ({
    entityName: "",

    isNeighbor2:false,

    isSearchSettingsShow:false,

    isRouteSearchShow: false,
    currentState: ZSTP_STATES.STATE_DISPLAY_GRAPH,

    routeOrig: "",
    routeDest: "",

    // 预先填充一个空元素，防止首屏渲染对话框时报错
    graphData: { nodes: [{ 
        label:"",
        labelUnwrapped:"",
        type:"",
        properties: [],
        neighbor1:[],
        neighbor2:[]
      }] },
    selectedNodeIndex: 0,

    alertData: {
      show: false,
      message: "",
      type: "primary",
    },
    alertOpacity: 0,

    isLoading: false,

    isDialogShow: false,
    dialogOpacity: 0,
    dialogWidth: isMobileBrowser() ? "80%" : "60%",

    // 0 to 100
    graphTransitionProgress: 0,
  }),
  mounted() {
    initGraph(document.getElementById("div-graph-container"), (e) => {
      if (e.nodes.length > 0) {
        this.selectedNodeIndex = this.graphData.nodes.findIndex(
          (x) => x.id === e.nodes[0]
        );

        if (this.currentState == ZSTP_STATES.STATE_ROUTE_ENTER_ORIG) {
          this.routeOrig =
            this.graphData.nodes[this.selectedNodeIndex].labelUnwrapped;
        } else if (this.currentState == ZSTP_STATES.STATE_ROUTE_ENTER_DEST) {
          this.routeDest =
            this.graphData.nodes[this.selectedNodeIndex].labelUnwrapped;
        } else {
          ANIMATIONS.showEntityDetail(this);
        }
      }
    });

    const tooltipTriggerList = [].slice.call(
      document.querySelectorAll('[data-bs-toggle="tooltip"]')
    );

    tooltipTriggerList.map(function (tooltipTriggerEl) {
      return new Tooltip(tooltipTriggerEl);
    });
  },
  methods: {
    inEntityNameClick(){
      this.isRouteSearchShow=false;
      this.isSearchSettingsShow=!this.isSearchSettingsShow;
      this.currentState = ZSTP_STATES.STATE_DISPLAY_GRAPH;
    },
    btnQueryClick() {
      this.queryGraph();
    },
    btnShowRouteClick() {
      this.isSearchSettingsShow=false;
      
      if (this.isRouteSearchShow) {
        this.isRouteSearchShow = false;
        this.currentState = ZSTP_STATES.STATE_DISPLAY_GRAPH;
      } else {
        this.isRouteSearchShow = true;
        this.currentState = ZSTP_STATES.STATE_ROUTE_ENTER_ORIG;
      }
    },
    inRouteOrigClick() {
      this.currentState = ZSTP_STATES.STATE_ROUTE_ENTER_ORIG;
    },
    inRouteDestClick() {
      this.currentState = ZSTP_STATES.STATE_ROUTE_ENTER_DEST;
    },
    closeNodeDetail() {
      this.isDialogShow = false;
    },
    exchangeOrigDest() {
      const orig = this.routeOrig;
      this.routeOrig = this.routeDest;
      this.routeDest = orig;
    },
    queryGraph() {
      if (this.isLoading || this.alertData.show) {
        return;
      }

      if (this.entityName === "") {
        ANIMATIONS_COMMON.showAlert(this, "请输入实体名称", "danger");
        return;
      }

      this.isLoading = true;

      this.isRouteSearchShow = false;
      this.currentState = ZSTP_STATES.STATE_DISPLAY_GRAPH;

      ANIMATIONS.startProgress(this);

      axios
        .get(HWMI_URLS.zstpRequestUrl, {
          params: {
            entityName: this.entityName,
            neighbor:this.isNeighbor2?2:1
          },
        })
        .then((res) => {
          this.graphData = res.data;

          if (res.data.nodes.length === 0) {
            ANIMATIONS_COMMON.showAlert(this, "未查询到数据", "warning");
          }

          wrapNodeLabel(res.data.nodes);
          ANIMATIONS.transitGraph(this, () => {
            updateGraph(res.data);
          });
        })
        .catch((reason) => {
          ANIMATIONS_COMMON.showAlert(this, reason, "danger");
          this.graphTransitionProgress = 0;
          this.isLoading = false;
        });
    },
    btnRouteSearchClick() {
      if (this.isLoading || this.alertData.show) {
        return;
      }

      if (this.routeOrig === "" || this.routeDest === "") {
        ANIMATIONS_COMMON.showAlert(this, "请输入起点和终点实体", "danger");
        return;
      }

      this.isLoading = true;

      ANIMATIONS.startProgress(this);

      axios
        .get(HWMI_URLS.zstpRouteRequestUrl, {
          params: {
            from: this.routeOrig,
            to: this.routeDest,
          },
        })
        .then((res) => {
          this.graphData = res.data;

          if (res.data.nodes.length === 0) {
            ANIMATIONS_COMMON.showAlert(this, "未查询到数据", "warning");
          }

          wrapNodeLabel(res.data.nodes);
          ANIMATIONS.transitGraph(this, () => {
            updateGraph(res.data, true);
          });
          this.currentState = ZSTP_STATES.STATE_DISPLAY_GRAPH;
        })
        .catch((reason) => {
          ANIMATIONS_COMMON.showAlert(this, reason, "danger");
          this.graphTransitionProgress = 0;
          this.isLoading = false;
        });
    },
    hideEntityDetail() {
      ANIMATIONS.hideEntityDetail(this);
    },
    setSearchEntity(name){
      this.entityName=name;
      this.isDialogShow=false;
    }
  },

  beforeRouteEnter(to, from, next) {
    next((vm) => {
      // this branch should not bu written outside.
      // This will make the page blank when not entering from jbzd.
      if (to.query.keyword === undefined) {
        return;
      }

      vm.entityName = to.query.keyword;
      vm.queryGraph();
    });
  },
};
</script>

<style lang="scss" scoped>
@import "../CommonStyles";
@import "../App";

$controls-wrapper-width: m#{i}n(380px, 90%);

@mixin graph-progress-margin($top: 5px, $x: 5px) {
  top: calc(#{$navbar-height} + #{$top});
  right: $x;
  width: calc(100% - #{$x} - #{$x});
}

@mixin zstp-button($bkg, $hov) {
  padding: 5px 15px;
  background-color: $bkg;

  &:hover {
    background-color: $hov;
  }

  transition-duration: 150ms;
}

.div-content-wrapper {
  @include full-parent-size;
  @include page-background-image("../assets/images/zstp-bkg.png");
}

.div-graph-wrapper {
  @include full-parent-size;
  z-index: $z-index-zstp-graph-wrapper;
}

.div-controls-wrapper {
  position: fixed;
  top: calc(#{$navbar-height} + 20px);
  left: 20px;

  width: $controls-wrapper-width;

  z-index: $z-index-zstp-controls-wrapper;
}

.div-graph-container {
  @include full-parent-size;
}

.div-graph-progress {
  position: absolute;
  height: 5px;

  @include graph-progress-margin(0, 0px);
}

.btn-route {
  @include zstp-button(white, #eee);
  border: 0;

  // 路径搜索按钮左边的灰色竖线
  &:before {
    content: "";

    height: 28px;

    position: absolute;
    right: 90px; // 缺陷：此处将其位置硬编码
    border-left: 1px solid #eee;
  }
}

.btn-query {
  border: 0;
  border-top-right-radius: 0.25rem;
  border-bottom-right-radius: 0.25rem;
  @include zstp-button(#0d6efd, #0b5ed7);
}

.div-search-wrapper {
  background: white;
  border-radius: 0.25rem;

  animation-name: fade-in-drop;
  animation-duration: 0.2s;
}

.div-switch-wrapper{
  padding: 8px 5px;
  width: 100%;
}

.in-neighbor-switch{
  margin: 0 5px !important;
  background-image: url("data:image/svg+xml,%3csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%27-4 -4 8 8%27%3e%3ccircle r=%273%27 fill=%27%23fff%27/%3e%3c/svg%3e") !important;
  background-color: #0d6efd;
  border-color: #0d6efd;
}

.div-route-input-wrapper {
  padding-right: 75px;
}

.btn-submit-route-search {
  @include zstp-button(white, #0d6efd);
  &:hover {
    color: white;
  }
  &:active {
    background-color: #0b5ed7;
  }
  padding: 0;
  font-size: 13px;
  width: 55px;
  height: 55px;
  border: 1px solid #0d6efd;
  border-radius: 50%;
  color: #0d6efd;

  position: absolute;
  right: 10px;
}

.i-search-neighbor{
  cursor: pointer;
  color: #0b5ed7;
}
</style>